<?php
/*
 * @Author: your name
 * @Date: 2021-12-29 09:18:45
 * @LastEditTime: 2022-03-02 17:15:05
 * @LastEditors: Please set LastEditors
 * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * @FilePath: /nlog/swoole_server/Http.php
 */
// use Swoole\Coroutine\MySQL;
// use function Swoole\Coroutine\run;

class Ws
{
    private  $host = '0.0.0.0';
    private  $port =  9501;
    public   $ws = null;
    public   $redis =  null;
    public   $kernel = null;
    public   $parttimeOnlineKey =  null;
    public function  __construct()
    {
        $this->ws = new Swoole\WebSocket\Server($this->host, $this->port);    //创建 swoole WebSocket http服务
        $this->ws->set([
            'enable_static_handler' => true,                              // 开启静态文件请求处理功能, 需配合document_root使用 用于文件访问 类似图片
            'document_root' =>  dirname(__DIR__) . '/public',   // 静态文件路径 
            'worker_num' => 4,                                          // swoole异步客户端的IO都是非阻塞的，属于CPU密集型操作。如果不清楚自己业务偏向于哪种类型，可设置进程数为CPU核数的2倍左右即可。

            'task_worker_num' => 8,                                       // 最大值不得超过SWOOLE_CPU_NUM * 1000 Task进程内不能使用swoole_mysql、swoole_redis、swoole_event等异步IO函数
            'open_http_protocol' => true,                                //开启http服务
            'open_tcp_keepalive' => true,                               //开启检测死连接服务
            'tcp_keepidle' => 10, //10s没有数据传输就进行检测
            'tcp_keepinterval' => 3, //3s探测一次
            'tcp_keepcount' => 5, //探测的次数，超过5次后还没回包close此连接

            'max_wait_time' => 60,   //设置 Worker 进程收到停止服务通知后最大等待时间【默认值：3】
            'reload_async' => true,  //设置异步重启开关。【默认值：true】

            // 'enable_coroutine' => false  //enable_coroutine 关闭时在事件回调函数中不再自动创建协程，如果不需要用协程关闭这个会提高一些性能。
            // 'package_max_length'=>128,
        ]);
        // Co::set(['hook_flags' => SWOOLE_HOOK_ALL]);

        $this->ws->on('start', [$this, "onStart"]);                   //swoole 开启时
        $this->ws->on('Open', [$this, "onOpen"]);                     //第一次连接时
        $this->ws->on('Message', [$this, "onMessage"]);               //接收message信息
        $this->ws->on('Close', [$this, "onClose"]);                   //关闭时

        $this->ws->on('WorkerStart', [$this, "onWorkerStart"]);   //进程开启

        $this->ws->on('Request', [$this, "onRequest"]);           //接收请求


        // $this->ws->on('Receive', [$this, 'onReceive']); //此回调函数在worker进程中执行
        $this->ws->on('Task', [$this, "onTask"]); //处理异步任务(此回调函数在task进程中执行)


        $this->ws->on('Finish', [$this, "onFinish"]); //处理异步任务的结果(此回调函数在worker进程中执行)


        $this->ws->start();
    }

    public function onStart()
    {
        swoole_set_process_name("swoole_server");   //开启swoole 服务时设置进程名称为 swoole_server
    }

  



    public function onWorkerStart($server,  $workerId)
    {

        define('LARAVEL_START', microtime(true));                                      //开启服务时自动加载
        

        include_once dirname(__FILE__) . '/Defind.php';                                       //常量配置
        include_once dirname(__FILE__) . '/Function.php';                                       //常量配置
        $this->redisConnection();                                                           //redis连接
        $this->parttimeOnlineKey =  __REDIS_ONLIE_KEY__;
        $this->redis->del($this->parttimeOnlineKey);                                             //每次重启删除在线集合
        if (file_exists(__DIR__ . '/../storage/framework/maintenance.php')) {
            require __DIR__ . '/../storage/framework/maintenance.php';
        }
        //使用composer的自动加载
        require __DIR__ . '/../vendor/autoload.php';

        $app = require __DIR__ . '/../bootstrap/app.php';          //注册http内核
        $this->kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);  //引导程序
    }

    public function onRequest($request, $response)
    {
      
        // $this->ws->reload();     //平滑重启


        $this->setHeader($response);   //设置跨域


        $_SERVER = []; //清理原有内存
        if (isset($request->server)) {
            foreach ($request->server as $k => $v) {
                $_SERVER[strtoupper($k)] = $v;
            }
        }

        if (isset($request->header)) {
            foreach ($request->header as $k => $v) {
                $_SERVER[strtoupper($k)] = $v;
            }
        }

        $_GET = [];  //清理原有内存
        if (isset($request->get)) {
            foreach ($request->get as $k => $v) {
                $_GET[$k] = $v;
            }
        }
        $_POST = [];  //清理原有内存
        if (isset($request->post)) {
            foreach ($request->post as $k => $v) {
                $_POST[$k] = $v;
            }
        }
        $_FILES = []; //清理原有内存
        if (empty($request->files)) {
            if (!empty($request->getContent())) {   //json数据
                $array = json_decode($request->getContent(), true);
                foreach ($array as $k => $v) {
                    $_POST[$k] = $v;
                }
            }
        } else {
            //文件上传
            foreach ($request->files as $k => $v) {
                $_FILES[$k] = $v;
            }
        }
        $_POST['REQUEST_ALL'] = array(
            'POST' => $_POST,
            'GET' => $_GET,
            'header' => $_SERVER,
        );


        $_POST['HTTP_SERVER'] = $response;
        $_POST['HTTP_SERVER_REQUEST'] = $request;
        $_POST['WEBSOCKET_SERVER'] = $this->ws;
        // $_POST['writeLogFunc'] = $this->writeLog;
        // $_POST['writeLogFunc']();
         ob_start();
        //开启缓冲区 当打开输出缓存后，php的输出被重定向到内存。待程序结束后，再一并输出到客户端。
        //处理输入输出返回状态 
        try {
            $laravel_response = $this->kernel->handle(
                $laravel_request = Illuminate\Http\Request::capture()
            )->send();
        } catch (Exception $e) {
            //todo
            echo  'fail';
        }

        $res = ob_get_contents();      //获取并保存缓冲数据
        ob_end_clean();    //关闭缓冲区
        //terminate中间件
         swooleWriteLogFunc(['request' => $_POST['REQUEST_ALL'], 'response' => $res]);//访问日志
        $this->kernel->terminate($laravel_request, $laravel_response);
        
        $response->end($res);             //输出
    }


    public function setHeader($response)
    {
        $response->header("Content-Type", "text/html; charset=utf-8");
        $response->header('Access-Control-Allow-Origin', '*');
        $response->header('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT, PATCH, OPTIONS');
        $response->header('Access-Control-Allow-Headers', '*');
        date_default_timezone_set('Asia/Chongqing');   //设置时间戳为中国时间

    }


    public function onOpen($ws, $request)
    {
        $this->redis->hMset($this->parttimeOnlineKey, [$request->fd => $request->get['project']]);              // 0为绑定为游客
        if ($request->get['project'] == 'parttime') {   //每日兼职
            $ws->push($request->fd, json_encode(['type' => 'login', 'source' => $request->fd]));
        }  //redis连接

    }

    public function onMessage($ws, $frame)
    {
        // $data = json_decode( $frame['data'] , 1);
        // if(isset($data['type'])){
        //     if($data['type'] == "HEART_BEAT"){  //不做操作
        //     }
        // }

    }

    public function onClose($ws, $fd)
    {

        if ($this->redis->hExists($this->parttimeOnlineKey, $fd)) {
            $this->redis->hDel($this->parttimeOnlineKey, $fd);  //在线集合删除
        }
    }

    public function onReceive($serv, $fd, $reactor_id, $data)
    {
        // $task_id = $serv->task($data);
        // echo "Dispatch AsyncTask: id={$task_id}\n";
    }

    public function onTask($serv, $task_id, $reactor_id, $data)
    {
       
    }

    public function onFinish($serv, $task_id, $data)
    {
        //    echo "AsyncTask[{$task_id}] Finish: {$data}".PHP_EOL;
    }
    
    


    public function redisConnection()
    {
        $this->redis = new Redis();
        $this->redis->connect(__REDIS_HOST__, __REDIS_PORT__);
        $this->redis->auth(__REDIS_PASSWORD__);
        $this->redis->select(12);                                                //每次重启删除在线集合
    }

  
}



$ws = new Ws();
